package com.common.Util;

public class NBase64 {
	
	private static final byte[] encodingTable = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
		(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
		(byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
		(byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
		(byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
private static final byte[] encodingTableN = {(byte) 'j', (byte) 'V',(byte) '5', (byte) '7', (byte) 'm', (byte) 'y', (byte) 'a', (byte) '3', (byte) 'F', (byte) 'Z', (byte) 'e',
		(byte) 'N', (byte) 'R', (byte) 'B', (byte) 'u',(byte) 'A', (byte) 'p', (byte) 'k', (byte) 'U', (byte) 'l', (byte) '6', (byte) '2', (byte) 'x',(byte) 'q', (byte) 'H', (byte) 'G',
		(byte) 'Y',(byte) 'M', (byte) 'f', (byte) 'Q', (byte) 'C', (byte) 't', (byte) 'o', (byte) 'T', (byte) 's', (byte) 'h', (byte) 'I', (byte) 'X', (byte) 'c', (byte) '1', 
		(byte) 'g', (byte) 'P', (byte) 'w', (byte) '9', (byte) 'L', (byte) 'D', (byte) 'S',(byte) 'b', (byte) 'n', (byte) 'i', (byte) 'r', (byte) '4', (byte) 'J', (byte) 'z', (byte) 'W', 
		(byte) 'd', (byte) '0', (byte) 'O', (byte) 'v', (byte) 'K', (byte) 'E', (byte) '8', (byte) '+', (byte) '/' };

private static final byte[] decodingTable;
static {
	decodingTable = new byte[128];
	for (int i = 0; i < 128; i++) {
		decodingTable[i] = (byte) -1;
	}
	for (int i = 'A'; i <= 'Z'; i++) {
		decodingTable[i] = changeEnCodeTable((byte) (i - 'A'));
	}
	for (int i = 'a'; i <= 'z'; i++) {
		decodingTable[i] = changeEnCodeTable((byte) (i - 'a' + 26));
	}
	for (int i = '0'; i <= '9'; i++) {
		decodingTable[i] = changeEnCodeTable((byte) (i - '0' + 52));
	}
	decodingTable['+'] = 62;
	decodingTable['/'] = 63;
}

private static byte changeDeCode(byte b) {
	return decodingTable[b];
}
private static byte changeEnCodeTable(byte b) {	
	
	byte ab = encodingTable[b];
	byte bb = b;
	int i = 0;
	for ( i = 0; i < 64; i++) {
		bb = encodingTableN[i];
		if (bb == ab) {
			break;
		}
	}
	return (byte) i;
}
private static byte changeEnCode(byte b) {
	return encodingTableN[b];
}

public static byte[] encode(byte[] data) {
	byte[] bytes;
	int modulus = data.length % 3;
	if (modulus == 0) {
		bytes = new byte[(4 * data.length) / 3];
	} else {
		bytes = new byte[4 * ((data.length / 3) + 1)];
	}
	int dataLength = (data.length - modulus);
	int a1;
	int a2;
	int a3;
	for (int i = 0, j = 0; i < dataLength; i += 3, j += 4) {
		a1 = data[i] & 0xff;
		a2 = data[i + 1] & 0xff;
		a3 = data[i + 2] & 0xff;
		bytes[j] = changeEnCode((byte)((a1 >>> 2) & 0x3f));
		bytes[j + 1] = changeEnCode((byte)(((a1 << 4) | (a2 >>> 4)) & 0x3f));
		bytes[j + 2] = changeEnCode((byte)(((a2 << 2) | (a3 >>> 6)) & 0x3f));
		bytes[j + 3] = changeEnCode((byte)(a3 & 0x3f));
	}
	int b1;
	int b2;
	int b3;
	int d1;
	int d2;
	switch (modulus) {
	case 0: /* nothing left to do */
		break;
	case 1:
		d1 = data[data.length - 1] & 0xff;
		b1 = (d1 >>> 2) & 0x3f;
		b2 = (d1 << 4) & 0x3f;
		bytes[bytes.length - 4] = changeEnCode((byte)(b1));
		bytes[bytes.length - 3] = changeEnCode((byte)(b2));
		bytes[bytes.length - 2] = (byte) '=';
		bytes[bytes.length - 1] = (byte) '=';
		break;
	case 2:
		d1 = data[data.length - 2] & 0xff;
		d2 = data[data.length - 1] & 0xff;
		b1 = (d1 >>> 2) & 0x3f;
		b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
		b3 = (d2 << 2) & 0x3f;
		bytes[bytes.length - 4] = changeEnCode((byte)(b1));
		bytes[bytes.length - 3] = changeEnCode((byte)(b2));
		bytes[bytes.length - 2] = changeEnCode((byte)(b3));
		bytes[bytes.length - 1] = (byte) '=';
		break;
	}
	return bytes;
}

public static byte[] decode(byte[] data) {
	byte[] bytes;
	byte b1;
	byte b2;
	byte b3;
	byte b4;
	data = discardNonBase64Bytes(data);
	if (data[data.length - 2] == '=') {
		bytes = new byte[(((data.length / 4) - 1) * 3) + 1];
	} else if (data[data.length - 1] == '=') {
		bytes = new byte[(((data.length / 4) - 1) * 3) + 2];
	} else {
		bytes = new byte[((data.length / 4) * 3)];
	}
	for (int i = 0, j = 0; i < (data.length - 4); i += 4, j += 3) {
		b1 = changeDeCode(data[i]);
		b2 = changeDeCode(data[i + 1]);
		b3 = changeDeCode(data[i + 2]);
		b4 = changeDeCode(data[i + 3]);
		bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
		bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
		bytes[j + 2] = (byte) ((b3 << 6) | b4);
	}
	if (data[data.length - 2] == '=') {
		b1 = changeDeCode(data[data.length - 4]);
		b2 = changeDeCode(data[data.length - 3]);
		bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
	} else if (data[data.length - 1] == '=') {
		b1 = changeDeCode(data[data.length - 4]);
		b2 = changeDeCode(data[data.length - 3]);
		b3 = changeDeCode(data[data.length - 2]);
		bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
		bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
	} else {
		b1 = changeDeCode(data[data.length - 4]);
		b2 = changeDeCode(data[data.length - 3]);
		b3 = changeDeCode(data[data.length - 2]);
		b4 = changeDeCode(data[data.length - 1]);
		bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
		bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
		bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
	}
	return bytes;
}

public static byte[] decode(String data) {
	byte[] bytes;
	byte b1;
	byte b2;
	byte b3;
	byte b4;
	data = discardNonBase64Chars(data);
	if (data.charAt(data.length() - 2) == '=') {
		bytes = new byte[(((data.length() / 4) - 1) * 3) + 1];
	} else if (data.charAt(data.length() - 1) == '=') {
		bytes = new byte[(((data.length() / 4) - 1) * 3) + 2];
	} else {
		bytes = new byte[((data.length() / 4) * 3)];
	}
	for (int i = 0, j = 0; i < (data.length() - 4); i += 4, j += 3) {
		b1 = changeDeCode((byte)data.charAt(i));
		b2 = changeDeCode((byte)data.charAt(i + 1));
		b3 = changeDeCode((byte)data.charAt(i + 2));
		b4 = changeDeCode((byte)data.charAt(i + 3));
		bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
		bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
		bytes[j + 2] = (byte) ((b3 << 6) | b4);
	}
	if (data.charAt(data.length() - 2) == '=') {
		b1 = changeDeCode((byte)data.charAt(data.length() - 4));
		b2 = changeDeCode((byte)data.charAt(data.length() - 3));
		bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
	} else if (data.charAt(data.length() - 1) == '=') {
		b1 = changeDeCode((byte)data.charAt(data.length() - 4));
		b2 = changeDeCode((byte)data.charAt(data.length() - 3));
		b3 = changeDeCode((byte)data.charAt(data.length() - 2));
		bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
		bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
	} else {
		b1 = changeDeCode((byte)data.charAt(data.length() - 4));
		b2 = changeDeCode((byte)data.charAt(data.length() - 3));
		b3 = changeDeCode((byte)data.charAt(data.length() - 2));
		b4 = changeDeCode((byte)data.charAt(data.length() - 1));
		bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
		bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
		bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
	}
	
	return bytes;
}

private static byte[] discardNonBase64Bytes(byte[] data) {
	byte[] temp = new byte[data.length];
	int bytesCopied = 0;
	for (int i = 0; i < data.length; i++) {
		if (isValidBase64Byte(data[i])) {
			temp[bytesCopied++] = data[i];
		}
	}
	byte[] newData = new byte[bytesCopied];
	System.arraycopy(temp, 0, newData, 0, bytesCopied);
	return newData;
}

private static String discardNonBase64Chars(String data) {
	StringBuffer sb = new StringBuffer();
	int length = data.length();
	for (int i = 0; i < length; i++) {
		if (isValidBase64Byte((byte) (data.charAt(i)))) {
			sb.append(data.charAt(i));
		}
	}
	return sb.toString();
}

private static boolean isValidBase64Byte(byte b) {
	if (b == '=') {
		return true;
	} else if ((b < 0) || (b >= 128)) {
		return false;
	} else if (changeDeCode(b) == -1) {
		return false;
	}
	return true;
}

//public static void main(String[] args) {
//	String data = "1";
//	byte[] result = NBase64.encode(data.getBytes());
//	System.out.println(data);
//	System.out.println(new String(result));
//	System.out.println(new String(NBase64.decode(new String(result))).length());
//	System.out.println(">>");
//	System.out.println(new String(Base64.decodeBase64(result)));
//}
public String encode(String password) {
	return new String(encode(password.getBytes()));
}

}
